package dipl.algorithm.math.primitive.op;

import dipl.algorithm.math.primitive.Point2d;
import dipl.algorithm.math.utility.FPMath;
import org.apfloat.Apfloat;
import org.apfloat.ApfloatMath;

/**
 * Class provides operations on points
 */
public class Point2dOps {

	//
	// PUBLIC METHODS
	//

	/**
	 * Calculates area(p,q,r)
	 * @param p
	 * @param q
	 * @param r
	 * @return area
	 */
	public static Apfloat Area( Point2d p, Point2d q, Point2d r ) {
		Apfloat a1 = q.x.subtract( p.x );
		Apfloat a2 = r.y.subtract( p.y );
		Apfloat a3 = q.y.subtract( p.y );
		Apfloat a4 = r.x.subtract( p.x );
		return ApfloatMath.multiplySubtract( a1, a2, a3, a4 );
	}

	/**
	 * Tests whether given three points are collinear
	 * @param a
	 * @param b
	 * @param c
	 * @return
	 */
	public static boolean Collinear( Point2d a, Point2d b, Point2d c ) {
		Apfloat t1 = c.x.multiply( a.y.subtract( b.y ) );
		Apfloat t2 = c.y.multiply( b.x.subtract( a.x ) );
		Apfloat t3 = a.x.multiply( b.y );
		Apfloat t4 = a.y.multiply( b.x );
		Apfloat res = ApfloatMath.sum( t1, t2, t3 );
		res = res.subtract( t4 );
		return res.compareTo( Apfloat.ZERO ) == 0.0;
	}

	/**
	 * Compares given two points lexicographically.
	 * first x-coordinates are compared and if both are equal the y-coordinates are compared.
	 * @param a
	 * @param b
	 * @return
	 */
	public static int ComparePointsLexicographically( Point2d a, Point2d b ) {
		int c = a.x.compareTo( b.x );
		if( c != 0 ) return c;
		c = a.y.compareTo( b.y );
		if( c != 0 ) return c;
		return 0;
	}

	/**
	 * Compares given two points lexicographically.
	 * first x-coordinates are compared and if both are equal the y-coordinates are compared.
	 * @param ax
	 * @param ay
	 * @param bx
	 * @param by
	 * @return
	 */
	public static int ComparePointsLexicographically( Apfloat ax, Apfloat ay, Apfloat bx, Apfloat by ) {
		int c = ax.compareTo( bx );
		if( c != 0 ) return c;
		c = ay.compareTo( by );
		if( c != 0 ) return c;
		return 0;
	}

	/**
	 * Returns of distance from a to b
	 * @param a
	 * @param b
	 * @return
	 */
	public static Apfloat Distance( FPMath fpMath, Point2d a, Point2d b ) {
		Apfloat x = fpMath.m.subtract( a.x, b.x );
		Apfloat y = fpMath.m.subtract( a.y, b.y );
		return fpMath.m.sqrt( fpMath.m.multiplyAdd( x, x, y, y ) );
	}

	/**
	 * Calculates dot product of vectors pointing to given two points
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static Apfloat DotProduct( Point2d p1, Point2d p2 ) {
		return ApfloatMath.multiplyAdd( p1.x, p2.x, p1.y, p2.y );
	}

	/**
	 * Tests whether or not lines (p1,p0) and (p2,p1) make a left turn at p1.
	 * @param p0
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static boolean MakeLeftTurn( Point2d p0, Point2d p1, Point2d p2 ) {
		return Area( p0, p1, p2 ).compareTo( Apfloat.ZERO ) > 0;
	}

	/**
	 * Tests whether or not lines (p1,p0) and (p2,p1) make a right turn at p1.
	 * @param p0
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static boolean MakeRightTurn( Point2d p0, Point2d p1, Point2d p2 ) {
		return Area( p0, p1, p2 ).compareTo( Apfloat.ZERO ) < 0;
	}

	/**
	 * Returns square of distance from a to b
	 * @param a
	 * @param b
	 * @return
	 */
	public static Apfloat SquareDistance( Point2d a, Point2d b ) {
		Apfloat x = a.x.subtract( b.x );
		Apfloat y = a.y.subtract( b.y );
		return ApfloatMath.multiplyAdd( x, x, y, y );
	}
}
